﻿//-WinterFramework © WinterFeather
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.EventSystems;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class UIManager : SingleMono<UIManager>
{
    private Canvas canvas;
    private Canvas frontCanvas, backCanvas, threeDCanvas;

    public bool isVR = false;
    public const bool IS_USE_RESOURCE = false;
    public bool isEditorMode = false;
    public bool isDebug = false;
    Vector2 uiResolution = new Vector2(1080, 1920);

    public Dictionary<int, BasePanel> activePlane;
    Dictionary<Type, GameObject> uiPanelPre;

    public readonly string resourcePath = "UIPanel/";
    public readonly string assetPath = "Assets/WResources/UIPanel/";
    public AssetBundle UIbundle => uIbundle;
    AssetBundle uIbundle;

    EventSystem eventSystem;
    EventSystem EventSystem => eventSystem;

    public Canvas Canvas
    {
        get
        {
            if (canvas == null)
            {
                InitCanvas();
            }
            return canvas;
        }
    }
    public Canvas FrontCanvas
    {
        get
        {
            if (frontCanvas == null)
            {
                InitFrontCanvas();
            }
            return frontCanvas;
        }
    }
    public Canvas BackCanvas
    {
        get
        {
            if (backCanvas == null)
            {
                InitBackCanvas();
            }
            return backCanvas;
        }
    }
    public Canvas ThreeDCanvas
    {
        get
        {
            if (threeDCanvas == null)
            {
                InitTreeDCanvas();
            }
            return threeDCanvas;
        }
    }
    new void Awake()
    {
        base.Awake();
        InitEveneSystem();
        InitSceneLoad();
        InitPanelPreGO();
        InitCanvas();
        InitData();
    }

    void InitEveneSystem() {
        GameObject go = new GameObject("EventSystem");
        go.transform.parent = transform;
        eventSystem = go.AddComponent<EventSystem>();
        StandaloneInputModule slm = go.AddComponent<StandaloneInputModule>();
        slm.inputActionsPerSecond = 10;
        slm.repeatDelay = 0.5f;
    }

    void InitSceneLoad()
    {
        UnityEngine.SceneManagement.SceneManager.sceneLoaded += OnSceneLoaded;
        UnityEngine.SceneManagement.SceneManager.sceneUnloaded += OnSceneUnLoaded;
    }
    private void OnSceneUnLoaded(Scene arg0)
    {
        ClearUI(CanvasType.Normal);
    }
    private void OnSceneLoaded(Scene arg0, LoadSceneMode arg1)
    {
        Debug.Log("UIMgr OnSceneLoaded");
    }
    void InitData()
    {
        activePlane = new Dictionary<int, BasePanel>();
    }
    void InitPanelPreGO()
    {
        uiPanelPre = new Dictionary<Type, GameObject>();
    }
    void InitCanvas()
    {
        if (!canvas)// && !isTest
        {
            GameObject canvasGO = new GameObject("Canvas");
            canvasGO.layer = 5;
            canvas = canvasGO.AddComponent<Canvas>();
            CanvasScaler cs = canvasGO.GetComponent<CanvasScaler>();
            if (!cs) cs = canvasGO.AddComponent<CanvasScaler>();
            cs.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
            cs.referenceResolution = uiResolution;
            cs.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
            cs.matchWidthOrHeight = 0;

            canvasGO.AddComponent(typeof(GraphicRaycaster));//AddComponent<GraphicRaycaster>();
            canvas.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
        }

        if (isVR)
        {
            RectTransform rt = canvas.GetComponent<RectTransform>();
            rt.localScale = Vector3.one * 0.01f;
            rt.position = Vector3.zero;
            canvas.GetComponent<Canvas>().renderMode = RenderMode.WorldSpace;
        }

        canvas.sortingOrder = -1;
        canvas.transform.SetParent(transform);
    }
    void InitFrontCanvas()
    {
        if (!frontCanvas)// && !isTest
        {
            GameObject canvasGO = new GameObject("FrontCanvas");
            canvasGO.layer = 5;
            frontCanvas = canvasGO.AddComponent<Canvas>();

            CanvasScaler cs = canvasGO.GetComponent<CanvasScaler>();
            if (!cs) cs = canvasGO.AddComponent<CanvasScaler>();

            cs.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
            cs.referenceResolution = uiResolution;
            cs.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
            cs.matchWidthOrHeight = 0;
            canvasGO.AddComponent(typeof(GraphicRaycaster));
            frontCanvas.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
        }

        if (isVR)
        {
            RectTransform rt = frontCanvas.GetComponent<RectTransform>();
            rt.localScale = Vector3.one * 0.01f;
            rt.position = Vector3.zero;
            frontCanvas.GetComponent<Canvas>().renderMode = RenderMode.WorldSpace;
        }

        frontCanvas.sortingOrder = 999;
        frontCanvas.transform.SetParent(transform);
    }
    void InitBackCanvas()
    {
        if (!backCanvas)// && !isTest
        {
            GameObject canvasGO = new GameObject("BackCanvas");
            canvasGO.layer = 5;
            backCanvas = canvasGO.AddComponent<Canvas>();
            canvasGO.AddComponent(typeof(CanvasScaler));
            canvasGO.AddComponent(typeof(GraphicRaycaster));
            backCanvas.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
        }

        if (isVR)
        {
            RectTransform rt = frontCanvas.GetComponent<RectTransform>();
            rt.localScale = Vector3.one * 0.01f;
            rt.position = Vector3.zero;
            backCanvas.GetComponent<Canvas>().renderMode = RenderMode.WorldSpace;
        }

        backCanvas.sortingOrder = -999;
        backCanvas.transform.SetParent(transform);
    }
    void InitTreeDCanvas()
    {
        if (!threeDCanvas)// && !isTest
        {
            GameObject canvasGO = new GameObject("TreeCanvas");
            canvasGO.layer = 5;
            threeDCanvas = canvasGO.AddComponent<Canvas>();
            canvasGO.AddComponent(typeof(CanvasScaler));//AddComponent<CanvasScaler>();
            canvasGO.AddComponent(typeof(GraphicRaycaster));//AddComponent<GraphicRaycaster>();
            threeDCanvas.GetComponent<Canvas>().renderMode = RenderMode.WorldSpace;
            RectTransform rt = threeDCanvas.GetComponent<RectTransform>();
            rt.localScale = Vector3.one * 0.01f;
            rt.position = Vector3.zero;
        }
        threeDCanvas.transform.SetParent(transform);
    }

    T OpenPlane<T>(Vector3 p, Vector3 s, int id = 0, CanvasType canvasType = CanvasType.Normal, Transform parent = null, bool isUseResource = IS_USE_RESOURCE) where T : BasePanel
    {
        int panelID = GetPanelID(typeof(T).GetHashCode(), id);

        bool isNoInit = (!activePlane.ContainsKey(panelID) || activePlane[panelID] == null);

        T panle;

        if (isNoInit)
        {
            panle = InitPlane<T>(panelID, id, canvasType, isUseResource);
            panle.Init();
        }

        activePlane[panelID].Open(p, s);

        panle = activePlane[panelID] as T;

        if (panle == null) return null;
        if (parent) panle.transform.SetParent(parent);

        return panle;
    }
    public T OpenPlaneNormal<T>() where T : BasePanel
    {
        return OpenPlane<T>(0, CanvasType.Normal, null, IS_USE_RESOURCE);
    }
    public T OpenPlane<T>(int id = 0, CanvasType canvasStyle = CanvasType.Normal, Transform parent = null, bool isUseResource = IS_USE_RESOURCE) where T : BasePanel
    {
        T panle = OpenPlane<T>(Vector3.zero, Vector3.one, id, canvasStyle, parent, isUseResource);
        return panle;
    }
    public T InitPlane<T>(int id = 0, CanvasType canvasType = CanvasType.Normal, Transform parent = null, bool isUseResource = IS_USE_RESOURCE) where T : BasePanel
    {
        int panelID = GetPanelID(typeof(T).GetHashCode(), id);

        bool isNoInit = (!activePlane.ContainsKey(panelID) || activePlane[panelID] == null);

        if (!isNoInit) return activePlane[panelID] as T;

        T panle = InitPlane<T>(panelID, id, canvasType, isUseResource);

        if (parent) panle.transform.SetParent(parent);

        panle.Close();

        return panle;
    }
    public T InitPlane<T>(int hashCode, int id = 0, CanvasType canvasType = CanvasType.Normal, bool isUseResource = IS_USE_RESOURCE) where T : BasePanel
    {
        T panle = InstantiatePanel<T>(GetCanvas(canvasType).transform, isUseResource);
        panle.canvasType = canvasType;
        panle.id = id;
        panle.hashCode = hashCode;
        activePlane.Add(hashCode, panle);
        return panle;
    }
    public Transform GetCanvas(CanvasType style)
    {
        switch (style)
        {
            case CanvasType.Front:
                return FrontCanvas.transform;
            case CanvasType.Back:
                return BackCanvas.transform;
            case CanvasType.ThreeD:
                return ThreeDCanvas.transform;
        }
        return Canvas.transform;
    }
    public void SetFollower(Transform orign, Transform followTarget)
    {
        FollowTarget ft = orign.gameObject.GetComponent<FollowTarget>();
        if (!ft)
        {
            ft = orign.gameObject.AddComponent<FollowTarget>();
        }
        ft.target = followTarget;
    }
    public T ClosePlane<T>(int id = 0) where T : BasePanel
    {
        int panelID = GetPanelID(typeof(T).GetHashCode(), id);
        if (activePlane.ContainsKey(panelID) && activePlane[panelID] != null)
        {
            activePlane[panelID].Close();  //GetComponent<T>().Close();
            return activePlane[panelID] as T;
        }
        return null;
    }
    public void ClearUI()
    {
        foreach (var item in activePlane)
        {
            Destroy(item.Value.gameObject);
        }
        activePlane.Clear();
    }
    public void ClearUI(CanvasType canvasType)
    {
        List<int> removeKey = new List<int>();
        foreach (var item in activePlane)
        {
            if (item.Value.canvasType == canvasType)
            {
                removeKey.Add(item.Key);
            }
        }

        for (int i = 0; i < removeKey.Count; i++)
        {
            Destroy(activePlane[removeKey[i]].gameObject);
            activePlane.Remove(removeKey[i]);
        }
    }
    public void ClearUIPre()
    {
        foreach (var item in uiPanelPre)
        {
            Destroy(item.Value);
        }
        uiPanelPre.Clear();
    }
    public void SetUIBundle(AssetBundle uiBundle)
    {
        this.uIbundle = uiBundle;
    }
    GameObject LoadUIPre<T>(bool isUseResource = IS_USE_RESOURCE)
    {
        GameObject go = null;
        if (isUseResource)
        {
            go = Resources.Load<GameObject>(resourcePath + typeof(T));
        }
        else if (isEditorMode && Application.isEditor)
        {
#if UNITY_EDITOR
            go = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath + typeof(T).ToString() + ".prefab");
#endif
        }
        else
        {
            go = uIbundle.LoadAsset<GameObject>(typeof(T).ToString());
        }
        if (go)
        {
            uiPanelPre.Add(typeof(T), go);
        }
        else
        {
            Debug.LogError("LoadUIPre<T> error, panel not found: " + typeof(T));
        }
        return go;
    }
    GameObject GetUIPre<T>(bool isUseResource = IS_USE_RESOURCE)
    {
        if (!uiPanelPre.ContainsKey(typeof(T)) || uiPanelPre[typeof(T)] == null)
        {
            LoadUIPre<T>(isUseResource);
        }
        return uiPanelPre[typeof(T)];
    }
    T InstantiatePanel<T>(Transform canvas, bool isUseResource = IS_USE_RESOURCE) where T : BasePanel
    {
        return Instantiate<GameObject>(GetUIPre<T>(isUseResource), canvas).GetComponent<T>();
    }
    public T GetPanel<T>(int id)
    {
        int key = GetPanelID(typeof(T).GetHashCode(), id);
        if (!activePlane.ContainsKey(key))
        {
            return default(T);
        }
        else
        {
            return activePlane[key].GetComponent<T>();
        }
    }
    public int GetPanelID(int typeHash, int id = 0)
    {
        return typeHash * 100000 + id % 999;
    }
    public void Align(RectTransform rectTransform, UIAlignType alignmentType)
    {
        RectTransform parentRectTransform = rectTransform.parent.GetComponent<RectTransform>();
        rectTransform.anchoredPosition = new Vector2(0, 0);
        rectTransform.pivot = new Vector2(0.5f, 0.5f);

        switch (alignmentType)
        {
            case UIAlignType.Top:
                rectTransform.anchoredPosition = new Vector2(rectTransform.anchoredPosition.x, parentRectTransform.sizeDelta.y / 2 - rectTransform.sizeDelta.y / 2);
                break;
            case UIAlignType.Bottom:
                rectTransform.anchoredPosition = new Vector2(rectTransform.anchoredPosition.x, -parentRectTransform.sizeDelta.y / 2 + rectTransform.sizeDelta.y / 2);
                break;
            case UIAlignType.Left:
                rectTransform.anchoredPosition = new Vector2(-parentRectTransform.sizeDelta.x / 2 + rectTransform.sizeDelta.x / 2, rectTransform.anchoredPosition.y);
                break;
            case UIAlignType.Right:
                rectTransform.anchoredPosition = new Vector2(parentRectTransform.sizeDelta.x / 2 - rectTransform.sizeDelta.x / 2, rectTransform.anchoredPosition.y);
                break;
            case UIAlignType.TopLeft:
                rectTransform.anchoredPosition = new Vector2(-parentRectTransform.sizeDelta.x / 2 + rectTransform.sizeDelta.x / 2, parentRectTransform.sizeDelta.y / 2 - rectTransform.sizeDelta.y / 2);
                break;
            case UIAlignType.TopRight:
                rectTransform.anchoredPosition = new Vector2(parentRectTransform.sizeDelta.x / 2 - rectTransform.sizeDelta.x / 2, parentRectTransform.sizeDelta.y / 2 - rectTransform.sizeDelta.y / 2);
                break;
            case UIAlignType.BottomLeft:
                rectTransform.anchoredPosition = new Vector2(-parentRectTransform.sizeDelta.x / 2 + rectTransform.sizeDelta.x / 2, -parentRectTransform.sizeDelta.y / 2 + rectTransform.sizeDelta.y / 2);
                break;
            case UIAlignType.BottomRight:
                rectTransform.anchoredPosition = new Vector2(parentRectTransform.sizeDelta.x / 2 - rectTransform.sizeDelta.x / 2, -parentRectTransform.sizeDelta.y / 2 + rectTransform.sizeDelta.y / 2);
                break;
            case UIAlignType.Center:
                rectTransform.anchoredPosition = new Vector2(0, 0);
                break;
        }
    }

    public class Debug:DebugHelper
    {
        public static void LogError(object s)
        {
            if(Instance.isDebug)
            UnityEngine.Debug.LogError(s);
        }
        public static void Log(object s)
        {
            if(Instance.isDebug)
            UnityEngine.Debug.Log(s);
        }
    }
}
public abstract class BasePanel : MonoBehaviour
{
    public int id;
    public int hashCode;
    public CanvasType canvasType;

    private RectTransform rectTransform;
    public Action<BasePanel> onOpenBefore, onOpenEnd, onCloseBefore, onCloseEnd;

    public bool isOpen;
    public RectTransform RectTransform
    {
        get
        {
            if (rectTransform == null) rectTransform = transform as RectTransform;
            return rectTransform;
        }
    }

    public virtual void Init()
    {
        
    }

    public virtual void Open(Vector3 pos, Vector3 size)
    {
        onOpenBefore?.Invoke(this);
        isOpen = true;
        gameObject.SetActive(true);
        SetPos(pos);
        transform.localScale = size;
        onOpenEnd?.Invoke(this);
    }

    public virtual void Close()
    {
        onCloseBefore?.Invoke(this);
        isOpen = false;
        gameObject.SetActive(false);
        onCloseEnd?.Invoke(this);
    }
    public void SetPos(Vector3 pos)
    {
        RectTransform.anchoredPosition3D = pos;
    }

    public void SetAnchors(UIPivot p) { 
    
    }

    #region SetPivot
    public void SetPivot(UIPivot p)
    {
        switch (p)
        {
            case UIPivot.Top:
                RectTransform.pivot = new Vector2(0.5f, 1); break;
            case UIPivot.Bottom:
                RectTransform.pivot = new Vector2(0.5f, 0); break;
            case UIPivot.Left:
                RectTransform.pivot = new Vector2(0, 0.5f); break;
            case UIPivot.Right:
                RectTransform.pivot = new Vector2(1, 0.5f); break;
            case UIPivot.Center:
                RectTransform.pivot = new Vector2(0.5f, 0.5f); break;
            case UIPivot.LeftUp:
                RectTransform.pivot = new Vector2(0, 1); break;
            case UIPivot.RigntUp:
                RectTransform.pivot = new Vector2(1, 1); break;
            case UIPivot.RightDown:
                RectTransform.pivot = new Vector2(1, 0); break;
            case UIPivot.LeftDown:
                RectTransform.pivot = new Vector2(0, 0); break;
            default:
                break;
        }
    }

    public void SetCustomPivot(Vector2 pos)
    {
        RectTransform.pivot = pos;
    }
    #endregion
}
public enum CanvasType
{
    Normal, Front, Back, ThreeD
}
public enum UIPivot
{
    Top, Bottom, Left, Right, LeftUp, RigntUp, LeftDown, RightDown, Center
}
public enum UIAlignType
{
    Top,
    Bottom,
    Left,
    Right,
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight,
    Center
}
public class FollowTarget : MonoBehaviour
{
    public Transform target;
    private void Update()
    {
        transform.position = target.position;
        transform.rotation = target.rotation;
    }
}